﻿#include "MonsterObject.h"
#include "Monsters/Deer.h"
#include "Monsters/Tree.h"
#include "Monsters/SpittingSpider.h"
#include "Monsters/CannibalPlant.h"
#include "Monsters/Guard.h"
#include "Monsters/CaveMaggot.h"
#include "Monsters/AxeSkeleton.h"
#include "Monsters/HarvestMonster.h"
#include "Monsters/FlamingWooma.h"
#include "Monsters/WoomaTaurus.h"
#include "Monsters/BugBagMaggot.h"
#include "Monsters/RedMoonEvil.h"
#include "Monsters/EvilCentipede.h"
#include "Monsters/ZumaMonster.h"
#include "Monsters/RedThunderZuma.h"
#include "Monsters/ZumaTaurus.h"
#include "Monsters/Shinsu.h"
#include "Monsters/KingScorpion.h"
#include "Monsters/DarkDevil.h"
#include "Monsters/IncarnatedGhoul.h"
#include "Monsters/IncarnatedZT.h"
#include "Monsters/BoneFamiliar.h"
#include "Monsters/DigOutZombie.h"
#include "Monsters/RevivingZombie.h"
#include "Monsters/ShamanZombie.h"
#include "Monsters/Khazard.h"
#include "Monsters/ToxicGhoul.h"
#include "Monsters/BoneSpearman.h"
#include "Monsters/BoneLord.h"
#include "Monsters/RightGuard.h"
#include "Monsters/LeftGuard.h"
#include "Monsters/MinotaurKing.h"
#include "Monsters/FrostTiger.h"
#include "Monsters/SandWorm.h"
#include "Monsters/Yimoogi.h"
#include "Monsters/CrystalSpider.h"
#include "Monsters/HolyDeva.h"
#include "Monsters/RootSpider.h"
#include "Monsters/BombSpider.h"
#include "Monsters/YinDevilNode.h"
#include "Monsters/OmaKing.h"
#include "Monsters/BlackFoxman.h"
#include "Monsters/RedFoxman.h"
#include "Monsters/WhiteFoxman.h"
#include "Monsters/TrapRock.h"
#include "Monsters/GuardianRock.h"
#include "Monsters/ThunderElement.h"
#include "Monsters/GreatFoxSpirit.h"
#include "Monsters/HedgeKekTal.h"
#include "Monsters/EvilMir.h"
#include "Monsters/EvilMirBody.h"
#include "Monsters/DragonStatue.h"
#include "Monsters/HumanWizard.h"
#include "Monsters/Trainer.h"
#include "Monsters/TownArcher.h"
#include "Monsters/HumanAssassin.h"
#include "Monsters/VampireSpider.h"
#include "Monsters/SpittingToad.h"
#include "Monsters/SnakeTotem.h"
#include "Monsters/CharmedSnake.h"
#include "IntelligentCreatureObject.h"
#include "Monsters/MutatedManworm.h"
#include "Monsters/CrazyManworm.h"
#include "Monsters/DarkDevourer.h"
#include "Monsters/Football.h"
#include "Monsters/PoisonHugger.h"
#include "Monsters/Hugger.h"
#include "Monsters/Behemoth.h"
#include "Monsters/FinialTurtle.h"
#include "Monsters/TurtleKing.h"
#include "Monsters/LightTurtle.h"
#include "Monsters/WitchDoctor.h"
#include "Monsters/HellSlasher.h"
#include "Monsters/HellPirate.h"
#include "Monsters/HellCannibal.h"
#include "Monsters/HellKeeper.h"
#include "Monsters/ConquestArcher.h"
#include "Monsters/Gate.h"
#include "Monsters/Wall.h"
#include "Monsters/Tornado.h"
#include "Monsters/WingedTigerLord.h"
#include "Monsters/FlamingMutant.h"
#include "Monsters/ManectricClaw.h"
#include "Monsters/ManectricBlest.h"
#include "Monsters/ManectricKing.h"
#include "Monsters/IcePillar.h"
#include "Monsters/TrollBomber.h"
#include "Monsters/TrollKing.h"
#include "Monsters/FlameSpear.h"
#include "Monsters/FlameMage.h"
#include "Monsters/FlameScythe.h"
#include "Monsters/FlameAssassin.h"
#include "Monsters/FlameQueen.h"
#include "Monsters/HellKnight.h"
#include "Monsters/HellLord.h"
#include "Monsters/HellBomb.h"
#include "Monsters/VenomSpider.h"
#include "Monsters/AncientBringer.h"
#include "Monsters/IceGuard.h"
#include "Monsters/ElementGuard.h"
#include "Monsters/DemonGuard.h"
#include "Monsters/KingGuard.h"
#include "Monsters/DeathCrawler.h"
#include "Monsters/BurningZombie.h"
#include "Monsters/MudZombie.h"
#include "Monsters/HardenRhino.h"
#include "Monsters/DemonWolf.h"
#include "Monsters/WhiteMammoth.h"
#include "Monsters/DarkBeast.h"
#include "Monsters/BloodBaboon.h"
#include "Monsters/CatWidow.h"
#include "Monsters/SandSnail.h"
#include "Monsters/BlackHammerCat.h"
#include "Monsters/StrayCat.h"
#include "Monsters/CatShaman.h"
#include "Monsters/Jar1.h"
#include "Monsters/Jar2.h"
#include "Monsters/SeedingsGeneral.h"
#include "Monsters/RestlessJar.h"
#include "Monsters/GeneralMeowMeow.h"
#include "Monsters/Armadillo.h"
#include "Monsters/ArmadilloElder.h"
#include "Monsters/TucsonMage.h"
#include "Monsters/TucsonWarrior.h"
#include "Monsters/TucsonEgg.h"
#include "Monsters/SwampWarrior.h"
#include "Monsters/CannibalTentacles.h"
#include "Monsters/TucsonGeneral.h"
#include "Monsters/GasToad.h"
#include "Monsters/Mantis.h"
#include "Monsters/AssassinBird.h"
#include "Monsters/RhinoPriest.h"
#include "Monsters/ElephantMan.h"
#include "Monsters/StoneGolem.h"
#include "Monsters/EarthGolem.h"
#include "Monsters/TreeGuardian.h"
#include "Monsters/TreeQueen.h"
#include "Monsters/PeacockSpider.h"
#include "Monsters/OmaCannibal.h"
#include "Monsters/OmaBlest.h"
#include "Monsters/OmaSlasher.h"
#include "Monsters/OmaMage.h"
#include "Monsters/OmaWitchDoctor.h"
#include "Monsters/CaveStatue.h"
#include "Monsters/PlagueCrab.h"
#include "Monsters/CreeperPlant.h"
#include "Monsters/Nadz.h"
#include "Monsters/AvengingSpirit.h"
#include "Monsters/AvengingWarrior.h"
#include "Monsters/AxePlant.h"
#include "Monsters/WoodBox.h"
#include "Monsters/SackWarrior.h"
#include "Monsters/HornedMage.h"
#include "Monsters/HornedArcher.h"
#include "Monsters/ColdArcher.h"
#include "Monsters/FloatingRock.h"
#include "Monsters/ScalyBeast.h"
#include "Monsters/Turtlegrass.h"
#include "Monsters/ManTree.h"
#include "Monsters/Bear.h"
#include "Monsters/FrozenFighter.h"
#include "Monsters/FrozenKnight.h"
#include "Monsters/IcePhantom.h"
#include "Monsters/SnowWolf.h"
#include "Monsters/BlackTortoise.h"
#include "Monsters/DragonWarrior.h"
#include "Monsters/Kirin.h"
#include "Monsters/FrozenMiner.h"
#include "Monsters/FrozenAxeman.h"
#include "Monsters/FrozenMagician.h"
#include "Monsters/SnowYeti.h"
#include "Monsters/IceCrystalSoldier.h"
#include "Monsters/DarkWraith.h"
#include "Monsters/StoningStatue.h"
#include "../MirEnvir/Envir.h"
#include "../Settings.h"
#include "../../Shared/ServerPackets.h"
#include "../MirDatabase/CharacterInfo.h"
#include "../../Shared/Functions/Functions.h"
#include "SpellObject.h"
#include "ItemObject.h"
#include "../../Shared/Globals.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
using namespace Server::MirObjects::Monsters;
namespace S = ServerPackets;

namespace Server::MirObjects
{

    MonsterObject *MonsterObject::GetMonster(MonsterInfo *info)
    {
        if (info == nullptr)
        {
            return nullptr;
        }

        switch (info->AI)
        {
            case 1:
            case 2:
                return new Deer(info);
            case 3:
                return new Tree(info);
            case 4:
                return new SpittingSpider(info);
            case 5:
                return new CannibalPlant(info);
            case 6:
                return new Guard(info);
            case 7:
                return new CaveMaggot(info);
            case 8:
                return new AxeSkeleton(info);
            case 9:
                return new HarvestMonster(info);
            case 10:
                return new FlamingWooma(info);
            case 11:
                return new WoomaTaurus(info);
            case 12:
                return new BugBagMaggot(info);
            case 13:
                return new RedMoonEvil(info);
            case 14:
                return new EvilCentipede(info);
            case 15:
                return new ZumaMonster(info);
            case 16:
                return new RedThunderZuma(info);
            case 17:
                return new ZumaTaurus(info);
            case 18:
                return new Shinsu(info);
            case 19:
                return new KingScorpion(info);
            case 20:
                return new DarkDevil(info);
            case 21:
                return new IncarnatedGhoul(info);
            case 22:
                return new IncarnatedZT(info);
            case 23:
                return new BoneFamiliar(info);
            case 24:
                return new DigOutZombie(info);
            case 25:
                return new RevivingZombie(info);
            case 26:
                return new ShamanZombie(info);
            case 27:
                return new Khazard(info);
            case 28:
                return new ToxicGhoul(info);
            case 29:
                return new BoneSpearman(info);
            case 30:
                return new BoneLord(info);
            case 31:
                return new RightGuard(info);
            case 32:
                return new LeftGuard(info);
            case 33:
                return new MinotaurKing(info);
            case 34:
                return new FrostTiger(info);
            case 35:
                return new SandWorm(info);
            case 36:
                return new Yimoogi(info);
            case 37:
                return new CrystalSpider(info);
            case 38:
                return new HolyDeva(info);
            case 39:
                return new RootSpider(info);
            case 40:
                return new BombSpider(info);
            case 41:
            case 42:
                return new YinDevilNode(info);
            case 43:
                return new OmaKing(info);
            case 44:
                return new BlackFoxman(info);
            case 45:
                return new RedFoxman(info);
            case 46:
                return new WhiteFoxman(info);
            case 47:
                return new TrapRock(info);
            case 48:
                return new GuardianRock(info);
            case 49:
                return new ThunderElement(info);
            case 50:
                return new GreatFoxSpirit(info);
            case 51:
                return new HedgeKekTal(info);
            case 52:
                return new EvilMir(info);
            case 53:
                return new EvilMirBody(info);
            case 54:
                return new DragonStatue(info);
            case 55:
                return new HumanWizard(info);
            case 56:
                return new Trainer(info);
            case 57:
                return new TownArcher(info);
            case 58:
                return new Guard(info);
            case 59:
                return new HumanAssassin(info);
            case 60:
                return new VampireSpider(info); //TODO - Clean up
            case 61:
                return new SpittingToad(info);
            case 62:
                return new SnakeTotem(info);
            case 63:
                return new CharmedSnake(info);
            case 64:
                return new IntelligentCreatureObject(info);
            case 65:
                return new MutatedManworm(info);
            case 66:
                return new CrazyManworm(info);
            case 67:
                return new DarkDevourer(info);
            case 68:
                return new Football(info);
            case 69:
                return new PoisonHugger(info);
            case 70:
                return new Hugger(info);
            case 71:
                return new Behemoth(info);
            case 72:
                return new FinialTurtle(info);
            case 73:
                return new TurtleKing(info);
            case 74:
                return new LightTurtle(info);
            case 75:
                return new WitchDoctor(info);
            case 76:
                return new HellSlasher(info);
            case 77:
                return new HellPirate(info);
            case 78:
                return new HellCannibal(info);
            case 79:
                return new HellKeeper(info);
            case 80:
                return new ConquestArcher(info);
            case 81:
                return new Gate(info);
            case 82:
                return new Wall(info);
            case 83:
                return new Tornado(info);
            case 84:
                return new WingedTigerLord(info);
            case 85:
                return new FlamingMutant(info); //TODO
            case 86:
                return new ManectricClaw(info);
            case 87:
                return new ManectricBlest(info);
            case 88:
                return new ManectricKing(info);
            case 89:
                return new IcePillar(info);
            case 90:
                return new TrollBomber(info);
            case 91:
                return new TrollKing(info);
            case 92:
                return new FlameSpear(info);
            case 93:
                return new FlameMage(info);
            case 94:
                return new FlameScythe(info);
            case 95:
                return new FlameAssassin(info);
            case 96:
                return new FlameQueen(info);
            case 97:
                return new HellKnight(info);
            case 98:
                return new HellLord(info);
            case 99:
                return new HellBomb(info);
            case 100:
                return new VenomSpider(info);
            case 101:
                return new AncientBringer(info);
            case 102:
                return new IceGuard(info);
            case 103:
                return new ElementGuard(info);
            case 104:
                return new DemonGuard(info);
            case 105:
                return new KingGuard(info);
            case 106:
                return new DeathCrawler(info);
            case 107:
                return new BurningZombie(info);
            case 108:
                return new MudZombie(info);
            case 109:
                return new HardenRhino(info);
            case 110:
                return new DemonWolf(info); //Effect 0/1
            case 111:
                return new WhiteMammoth(info);
            case 112:
                return new DarkBeast(info);
            case 113:
                return new BloodBaboon(info);
            case 114:
                return new CatWidow(info);
            case 115:
                return new SandSnail(info);
            case 116:
                return new BlackHammerCat(info);
            case 117:
                return new StrayCat(info);
            case 118:
                return new CatShaman(info);
            case 168:
                return new Jar1(info);
            case 169:
                return new Jar2(info);
            case 119:
                return new SeedingsGeneral(info);
            case 170:
                return new RestlessJar(info);
            case 120:
                return new GeneralMeowMeow(info);
            case 121:
                return new Armadillo(info);
            case 122:
                return new ArmadilloElder(info);
            case 123:
                return new TucsonMage(info);
            case 124:
                return new TucsonWarrior(info);
            case 125:
                return new TucsonEgg(info); //Effect 0/1
            case 126:
                return new SwampWarrior(info);
            case 127:
                return new CannibalTentacles(info);
            case 128:
                return new TucsonGeneral(info);
            case 129:
                return new GasToad(info);
            case 130:
                return new Mantis(info);
            case 131:
                return new AssassinBird(info);

            //132: Empty AI

            case 133:
                return new RhinoPriest(info);
            case 134:
                return new ElephantMan(info);
            case 135:
                return new StoneGolem(info);
            case 136:
                return new EarthGolem(info);
            case 137:
                return new TreeGuardian(info);
            case 138:
                return new TreeQueen(info);
            case 139:
                return new PeacockSpider(info); //TODO
            case 140:
                return new OmaCannibal(info);
            case 141:
                return new OmaBlest(info);
            case 142:
                return new OmaSlasher(info);
            case 143:
                return new OmaMage(info);

            case 144:
                return new OmaWitchDoctor(info); //TODO

            //145: TODO - PowerUpBead(s)

            //146: TODO - DarkOmaKing

            case 147:
                return new CaveStatue(info);
            case 148:
                return new PlagueCrab(info);
            case 149:
                return new CreeperPlant(info);
            case 150:
                return new Nadz(info);
            case 151:
                return new AvengingSpirit(info);
            case 152:
                return new AvengingWarrior(info);
            case 153:
                return new AxePlant(info);
            case 154:
                return new WoodBox(info);

            //case 155: TODO - ClawBeast

            //case 156: TODO - KillerPlant

            case 157:
                return new SackWarrior(info);

            //case 158: TODO - KingHydrax

            case 159:
                return new HornedMage(info);
            case 160:
                return new HornedArcher(info); //TODO
            case 161:
                return new ColdArcher(info); //TODO (See video, Arrow fires up)

            //case 162: TODO - HornedWarrior

            case 163:
                return new FloatingRock(info);
            case 164:
                return new ScalyBeast(info);



            case 212:
                return new TurtleGrass(info);
            case 213:
                return new ManTree(info);
            case 215:
                return new Bear(info);

            case 214:
                return new FrozenFighter(info);
            case 216:
                return new FrozenKnight(info);
            case 217:
                return new IcePhantom(info);
            case 218:
                return new SnowWolf(info);
            case 219:
                return new BlackTortoise(info);
            case 220:
                return new DragonWarrior(info);
            case 222:
                return new Kirin(info);
            case 223:
                return new FrozenMiner(info);
            case 224:
                return new FrozenAxeman(info);
            case 225:
                return new FrozenMagician(info);
            case 226:
                return new SnowYeti(info);
            case 227:
                return new IceCrystalSoldier(info);
            case 228:
                return new DarkWraith(info);

            case 254:
                return new StoningStatue(info); //TODO

            default:
                return new MonsterObject(info);
        }
    }

    ObjectType MonsterObject::getRace() const
    {
        return ObjectType::Monster;
    }

    std::string MonsterObject::getName() const
    {
        return Master == nullptr ? Info->getGameName() : StringHelper::formatSimple("{0}({1})", Info->getGameName(), Master->getName());
    }

    void MonsterObject::setName(const std::string &value)
    {
        throw NotSupportedException();
    }

    int MonsterObject::getCurrentMapIndex() const
    {
        return CurrentMapIndex;
    }

    void MonsterObject::setCurrentMapIndex(int value)
    {
        CurrentMapIndex = value;
    }

    Point *MonsterObject::getCurrentLocation() const
    {
        return CurrentLocation;
    }

    void MonsterObject::setCurrentLocation(Point *value)
    {
        CurrentLocation = value;
    }

    MirDirection MonsterObject::getDirection() const
    {
        return Direction;
    }

    void MonsterObject::setDirection(MirDirection value)
    {
        Direction = value;
    }

    unsigned short MonsterObject::getLevel() const
    {
        return Info->Level;
    }

    void MonsterObject::setLevel(unsigned short value)
    {
        throw NotSupportedException();
    }

    AttackMode MonsterObject::getAMode() const
    {
        return MapObject::getAMode();
    }

    void MonsterObject::setAMode(AttackMode value)
    {
        MapObject::setAMode(value);
    }

    PetMode MonsterObject::getPMode() const
    {
        return MapObject::getPMode();
    }

    void MonsterObject::setPMode(PetMode value)
    {
        MapObject::setPMode(value);
    }

    int MonsterObject::getHealth() const
    {
        return HP;
    }

    int MonsterObject::getMaxHealth() const
    {
        return Stats[Stat::HP];
    }

    unsigned int MonsterObject::getExperience() const
    {
        return Info->Experience;
    }

    int MonsterObject::getDeadDelay() const
    {
        switch (Info->AI)
        {
            case 64:
                return 0;
            case 81:
            case 82:
                return std::numeric_limits<int>::max();
            case 252:
                return 5000;
            default:
                return 180000;
        }
    }

    bool MonsterObject::getBlocking() const
    {
        return !Dead;
    }

    bool MonsterObject::getCanRegen() const
    {
        return getEnvir()->getTime() >= RegenTime;
    }

    bool MonsterObject::getCanMove() const
    {
        return !Dead && getEnvir()->getTime() > MoveTime && getEnvir()->getTime() > ActionTime && getEnvir()->getTime() > ShockTime && (Master == nullptr || Master->getPMode() == PetMode::MoveOnly || Master->getPMode() == PetMode::Both) && !CurrentPoison::HasFlag(PoisonType::Paralysis) && !CurrentPoison::HasFlag(PoisonType::LRParalysis) && !CurrentPoison::HasFlag(PoisonType::Stun) && !CurrentPoison::HasFlag(PoisonType::Frozen);
    }

    bool MonsterObject::getCanAttack() const
    {
        return !Dead && getEnvir()->getTime() > AttackTime && getEnvir()->getTime() > ActionTime && (Master == nullptr || Master->getPMode() == PetMode::AttackOnly || Master->getPMode() == PetMode::Both || !getCurrentMap()->Info->NoFight) && !CurrentPoison::HasFlag(PoisonType::Paralysis) && !CurrentPoison::HasFlag(PoisonType::LRParalysis) && !CurrentPoison::HasFlag(PoisonType::Stun) && !CurrentPoison::HasFlag(PoisonType::Dazed) && !CurrentPoison::HasFlag(PoisonType::Frozen);
    }

    MonsterObject::MonsterObject(MonsterInfo *info)
    {
        Info = info;

        Stats = new Stats();

        Undead = Info->Undead;
        AutoRev = info->AutoRev;
        CoolEye = info->CoolEye > getEnvir()->Random->Next(100);
        setDirection(static_cast<MirDirection>(getEnvir()->Random->Next(8)));

        setAMode(AttackMode::All);
        setPMode(PetMode::Both);

        RegenTime = getEnvir()->Random->Next(RegenDelay) + getEnvir()->getTime();
        SearchTime = getEnvir()->Random->Next(SearchDelay) + getEnvir()->getTime();
        RoamTime = getEnvir()->Random->Next(RoamDelay) + getEnvir()->getTime();
    }

    bool MonsterObject::Spawn(Map *temp, Point *location)
    {
        if (!temp->ValidPoint(location))
        {
            return false;
        }

        setCurrentMap(temp);
        setCurrentLocation(location);

        getCurrentMap()->AddObject(this);

        RefreshAll();
        SetHP(Stats[Stat::HP]);

        Spawned();
        getEnvir()->MonsterCount++;
        getCurrentMap()->MonsterCount++;
        return true;
    }

    bool MonsterObject::Spawn(MapRespawn *respawn)
    {
        Respawn = respawn;

        if (Respawn->Map == nullptr)
        {
            return false;
        }

        for (int i = 0; i < 10; i++)
        {
            Point tempVar(Respawn->Info->Location->X + getEnvir()->Random->Next(-Respawn->Info->Spread, Respawn->Info->Spread + 1), Respawn->Info->Location->Y + getEnvir()->Random->Next(-Respawn->Info->Spread, Respawn->Info->Spread + 1));
            setCurrentLocation(&tempVar);

            if (!respawn->Map->ValidPoint(getCurrentLocation()))
            {
                continue;
            }

            respawn->Map->AddObject(this);

            setCurrentMap(respawn->Map);

            if (Respawn->Route.size() > 0)
            {
                Route.insert(Route.end(), Respawn->Route.begin(), Respawn->Route.end());
            }

            RefreshAll();
            SetHP(Stats[Stat::HP]);

            Spawned();
            Respawn->Count++;
            respawn->Map->MonsterCount++;
            getEnvir()->MonsterCount++;
            return true;
        }

        return false;
    }

    void MonsterObject::Spawned()
    {
        ActionTime = getEnvir()->getTime() + 2000;
        if (Info->HasSpawnScript && (getEnvir()->MonsterNPC != nullptr))
        {
            getEnvir()->MonsterNPC->Call(this,StringHelper::formatSimple("[@_SPAWN({0})]",Info->Index));
        }

        MapObject::Spawned();
    }

    void MonsterObject::RefreshBase()
    {
        Stats->Clear();

        Stats->Add(Info->Stats);

        MoveSpeed = Info->MoveSpeed;
        AttackSpeed = Info->AttackSpeed;
    }

    void MonsterObject::RefreshAll()
    {
        RefreshBase();

        Stats[Stat::HP] += PetLevel * 20;
        Stats[Stat::MinAC] += PetLevel * 2;
        Stats[Stat::MaxAC] += PetLevel * 2;
        Stats[Stat::MinMAC] += PetLevel * 2;
        Stats[Stat::MaxMAC] += PetLevel * 2;
        Stats[Stat::MinDC] += PetLevel;
        Stats[Stat::MaxDC] += PetLevel;

        if (Info->Name == Settings::SkeletonName || Info->Name == Settings::ShinsuName || Info->Name == Settings::AngelName)
        {
            MoveSpeed = static_cast<unsigned short>(std::min(std::numeric_limits<unsigned short>::max(), (std::max(std::numeric_limits<unsigned short>::min(), MoveSpeed - MaxPetLevel * 130))));
            AttackSpeed = static_cast<unsigned short>(std::min(std::numeric_limits<unsigned short>::max(), (std::max(std::numeric_limits<unsigned short>::min(), AttackSpeed - MaxPetLevel * 70))));
        }

        if (MoveSpeed < 400)
        {
            MoveSpeed = 400;
        }
        if (AttackSpeed < 400)
        {
            AttackSpeed = 400;
        }

        RefreshBuffs();
    }

    void MonsterObject::RefreshBuffs()
    {
        for (int i = 0; i < Buffs.size(); i++)
        {
            Buff *buff = Buffs[i];

            Stats->Add(buff->Stats);

            switch (buff->Type)
            {
                case BuffType::SwiftFeet:
                    MoveSpeed = static_cast<unsigned short>(std::max(std::numeric_limits<unsigned short>::min(), MoveSpeed + 100));
                    break;
            }
        }
    }

    void MonsterObject::RefreshNameColour(bool send)
    {
        if (ShockTime < getEnvir()->getTime())
        {
            BindingShotCenter = false;
        }

        Color *colour = Color::White;

        switch (PetLevel)
        {
            case 1:
                colour = Color::Aqua;
                break;
            case 2:
                colour = Color::Aquamarine;
                break;
            case 3:
                colour = Color::LightSeaGreen;
                break;
            case 4:
                colour = Color::SlateBlue;
                break;
            case 5:
                colour = Color::SteelBlue;
                break;
            case 6:
                colour = Color::Blue;
                break;
            case 7:
                colour = Color::Navy;
                break;
        }

        if (getEnvir()->getTime() < ShockTime)
        {
            colour = Color::Peru;
        }
        else if (getEnvir()->getTime() < RageTime)
        {
            colour = Color::Red;
        }
        else if (getEnvir()->getTime() < HallucinationTime)
        {
            colour = Color::MediumOrchid;
        }

        if (colour == NameColour || !send)
        {
            return;
        }

        NameColour = colour;

        S::ObjectColourChanged *tempVar = new S::ObjectColourChanged();
        tempVar->ObjectID = ObjectID;
        tempVar->NameColour = NameColour;
        Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    void MonsterObject::SetHP(int amount)
    {
        if (HP == amount)
        {
            return;
        }

        HP = amount <= Stats[Stat::HP] ? amount : Stats[Stat::HP];

        if (!Dead && HP == 0)
        {
            Die();
        }

        //  HealthChanged = true;
        BroadcastHealthChange();
    }

    void MonsterObject::ChangeHP(int amount)
    {
        if (HP + amount > Stats[Stat::HP])
        {
            amount = Stats[Stat::HP] - HP;
        }

        if (amount == 0)
        {
            return;
        }

        HP += amount;

        if (HP < 0)
        {
            HP = 0;
        }

        if (!Dead && HP == 0)
        {
            Die();
        }

        // HealthChanged = true;
        BroadcastHealthChange();
    }

    void MonsterObject::PoisonDamage(int amount, MapObject *Attacker)
    {
        ChangeHP(amount);
    }

    bool MonsterObject::Teleport(Map *temp, Point *location, bool effects, unsigned char effectnumber)
    {
        if (temp == nullptr || !temp->ValidPoint(location))
        {
            return false;
        }

        getCurrentMap()->RemoveObject(this);
        if (effects)
        {
            S::ObjectTeleportOut *tempVar = new S::ObjectTeleportOut();
            tempVar->ObjectID = ObjectID;
            tempVar->Type = effectnumber;
            Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }
        S::ObjectRemove *tempVar2 = new S::ObjectRemove();
        tempVar2->ObjectID = ObjectID;
        Broadcast(tempVar2);

        getCurrentMap()->MonsterCount--;

        setCurrentMap(temp);
        setCurrentLocation(location);

        getCurrentMap()->MonsterCount++;

        setInTrapRock(false);

        getCurrentMap()->AddObject(this);
        BroadcastInfo();

        if (effects)
        {
            S::ObjectTeleportIn *tempVar3 = new S::ObjectTeleportIn();
            tempVar3->ObjectID = ObjectID;
            tempVar3->Type = effectnumber;
            Broadcast(tempVar3);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar3' statement was not added since tempVar3 was passed to a method or constructor. Handle memory management manually.
        }

        BroadcastHealthChange();

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
        return true;
    }

    void MonsterObject::Die()
    {
        if (Dead)
        {
            return;
        }

        HP = 0;
        Dead = true;

        DeadTime = getEnvir()->getTime() + getDeadDelay();

        S::ObjectDied *tempVar = new S::ObjectDied();
        tempVar->ObjectID = ObjectID;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        Broadcast(tempVar);

        if (Info->HasDieScript && (getEnvir()->MonsterNPC != nullptr))
        {
            getEnvir()->MonsterNPC->Call(this, StringHelper::formatSimple("[@_DIE({0})]", Info->Index));
        }

        if (EXPOwner != nullptr && Master == nullptr && EXPOwner->getRace() == ObjectType::Player)
        {
            EXPOwner->WinExp(getExperience(), getLevel());

            PlayerObject *playerObj = static_cast<PlayerObject*>(EXPOwner);
            playerObj->CheckGroupQuestKill(Info);
        }

        if (Respawn != nullptr)
        {
            Respawn->Count--;
        }

        if (Master == nullptr && EXPOwner != nullptr)
        {
            Drop();
        }

        Master = nullptr;

        PoisonList.clear();
        getEnvir()->MonsterCount--;
        getCurrentMap()->MonsterCount--;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    void MonsterObject::Revive(int hp, bool effect)
    {
        if (!Dead)
        {
            return;
        }

        SetHP(hp);

        Dead = false;
        ActionTime = getEnvir()->getTime() + RevivalDelay;

        S::ObjectRevived *tempVar = new S::ObjectRevived();
        tempVar->ObjectID = ObjectID;
        tempVar->Effect = effect;
        Broadcast(tempVar);

        if (Respawn != nullptr)
        {
            Respawn->Count++;
        }

        getEnvir()->MonsterCount++;
        getCurrentMap()->MonsterCount++;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    int MonsterObject::Pushed(MapObject *pusher, MirDirection dir, int distance)
    {
        if (!Info->CanPush)
        {
            return 0;
        }
        //if (!CanMove) return 0; //stops mobs that can't move (like cannibalplants) from being pushed

        int result = 0;
        MirDirection reverse = Functions::ReverseDirection(dir);
        for (int i = 0; i < distance; i++)
        {
            Point *location = Functions::PointMove(getCurrentLocation(), dir, 1);

            if (!getCurrentMap()->ValidPoint(location))
            {
                return result;
            }

            Cell *cell = getCurrentMap()->GetCell(location);

            bool stop = false;
            if (!cell->Objects.empty())
            {
                for (int c = 0; c < cell->Objects.size(); c++)
                {
                    MapObject *ob = cell->Objects[c];
                    if (!ob->getBlocking())
                    {
                        continue;
                    }
                    stop = true;
                }
            }
            if (stop)
            {
                break;
            }

            getCurrentMap()->GetCell(getCurrentLocation())->Remove(this);

            setDirection(reverse);
            RemoveObjects(dir, 1);
            setCurrentLocation(location);
            getCurrentMap()->GetCell(getCurrentLocation())->Add(this);
            AddObjects(dir, 1);

            S::ObjectPushed *tempVar = new S::ObjectPushed();
            tempVar->ObjectID = ObjectID;
            tempVar->Direction = getDirection();
            tempVar->Location = getCurrentLocation();
            Broadcast(tempVar);

            result++;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }

        ActionTime = getEnvir()->getTime() + 300 * result;
        MoveTime = getEnvir()->getTime() + 500 * result;

        if (result > 0)
        {
            Cell *cell = getCurrentMap()->GetCell(getCurrentLocation());

            for (int i = 0; i < cell->Objects.size(); i++)
            {
                if (cell->Objects[i]->getRace() != ObjectType::Spell)
                {
                    continue;
                }
                SpellObject *ob = static_cast<SpellObject*>(cell->Objects[i]);

                ob->ProcessSpell(this);
                //break;
            }
        }

        return result;
    }

    void MonsterObject::Drop()
    {
        if (getCurrentMap()->Info->NoDropMonster)
        {
            return;
        }
        for (int i = 0; i < Info->Drops.size(); i++)
        {
            DropInfo *drop = Info->Drops[i];

            int rate = static_cast<int>(drop->Chance / (Settings::DropRate));

            if (EXPOwner != nullptr && EXPOwner->Stats[Stat::ItemDropRatePercent] > 0)
            {
                rate -= (rate * EXPOwner->Stats[Stat::ItemDropRatePercent]) / 100;
            }

            if (rate < 1)
            {
                rate = 1;
            }

            if (getEnvir()->Random->Next(rate) != 0)
            {
                continue;
            }

            if (drop->Gold > 0)
            {
                int lowerGoldRange = static_cast<int>(drop->Gold / 2);
                int upperGoldRange = static_cast<int>(drop->Gold + drop->Gold / 2);

                if (EXPOwner != nullptr && EXPOwner->Stats[Stat::GoldDropRatePercent] > 0)
                {
                    lowerGoldRange += (lowerGoldRange * EXPOwner->Stats[Stat::GoldDropRatePercent]) / 100;
                }

                if (lowerGoldRange > upperGoldRange)
                {
                    lowerGoldRange = upperGoldRange;
                }

                int gold = getEnvir()->Random->Next(lowerGoldRange, upperGoldRange);

                if (gold <= 0)
                {
                    continue;
                }

                if (!DropGold(static_cast<unsigned int>(gold)))
                {
                    return;
                }
            }
            else
            {
                UserItem *item = getEnvir()->CreateDropItem(drop->Item);

                if (item == nullptr)
                {
                    continue;
                }

                if (EXPOwner != nullptr && EXPOwner->getRace() == ObjectType::Player)
                {
                    PlayerObject *ob = static_cast<PlayerObject*>(EXPOwner);

                    if (ob->CheckGroupQuestItem(item))
                    {
                        continue;
                    }
                }

                if (drop->QuestRequired)
                {
                    continue;
                }
                if (!DropItem(item))
                {
                    return;
                }
            }
        }
    }

    bool MonsterObject::DropItem(UserItem *item)
    {
        if (getCurrentMap()->Info->NoDropMonster)
        {
            return false;
        }

        ItemObject *ob = new ItemObject(this, item);
        ob->Owner = EXPOwner;
        ob->OwnerTime = getEnvir()->getTime() + Settings::Minute;

        if (!item->Info->GlobalDropNotify)
        {
            delete ob;
            return ob->Drop(Settings::DropRange);
        }

        for (auto player : getEnvir()->Players)
        {
            player->ReceiveChat(StringHelper::formatSimple("{0} has dropped {1}.", getName(), item->getFriendlyName()), ChatType::System2);
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was used in a 'return' or 'throw' statement.
        return ob->Drop(Settings::DropRange);
    }

    bool MonsterObject::DropGold(unsigned int gold)
    {
        if (EXPOwner != nullptr && EXPOwner->CanGainGold(gold) && !Settings::DropGold)
        {
            EXPOwner->WinGold(gold);
            return true;
        }

        unsigned int count = gold / Settings::MaxDropGold == 0 ? 1 : gold / Settings::MaxDropGold + 1;
        for (int i = 0; i < count; i++)
        {
            ItemObject *ob = new ItemObject(this, i != count - 1 ? Settings::MaxDropGold : gold % Settings::MaxDropGold);
            ob->Owner = EXPOwner;
            ob->OwnerTime = getEnvir()->getTime() + Settings::Minute;

            ob->Drop(Settings::DropRange);

            delete ob;
        }

        return true;
    }

    void MonsterObject::Process()
    {
        MapObject::Process();

        RefreshNameColour();

        if (getTarget() != nullptr && (getTarget()->getCurrentMap() != getCurrentMap() || !getTarget()->IsAttackTarget(this) || !Functions::InRange(getCurrentLocation(), getTarget()->getCurrentLocation(), Globals::DataRange)))
        {
            setTarget(nullptr);
        }

        for (int i = SlaveList.size() - 1; i >= 0; i--)
        {
            if (SlaveList[i]->Dead || SlaveList[i]->Node == nullptr)
            {
                SlaveList.erase(SlaveList.begin() + i);
            }
        }

        if (Dead && getEnvir()->getTime() >= DeadTime)
        {
            getCurrentMap()->RemoveObject(this);
            if (Master != nullptr)
            {
                Master->Pets.Remove(this);
                Master = nullptr;
            }

            Despawn();
            return;
        }

        if (Master != nullptr && TameTime > 0 && getEnvir()->getTime() >= TameTime)
        {
            Master->Pets.Remove(this);
            Master = nullptr;
            S::ObjectName *tempVar = new S::ObjectName();
            tempVar->ObjectID = ObjectID;
            tempVar->Name = getName();
            Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }

        ProcessAI();

        ProcessBuffs();
        ProcessRegen();
        ProcessPoison();


        /*   if (!HealthChanged) return;
    
           HealthChanged = false;
    
           BroadcastHealthChange();*/
    }

    void MonsterObject::SetOperateTime()
    {
        long long time = getEnvir()->getTime() + 2000;

        if (AloneTime < time && AloneTime > getEnvir()->getTime())
        {
            time = AloneTime;
        }

        if (DeadTime < time && DeadTime > getEnvir()->getTime())
        {
            time = DeadTime;
        }

        if (OwnerTime < time && OwnerTime > getEnvir()->getTime())
        {
            time = OwnerTime;
        }

        if (ExpireTime < time && ExpireTime > getEnvir()->getTime())
        {
            time = ExpireTime;
        }

        if (PKPointTime < time && PKPointTime > getEnvir()->getTime())
        {
            time = PKPointTime;
        }

        if (LastHitTime < time && LastHitTime > getEnvir()->getTime())
        {
            time = LastHitTime;
        }

        if (EXPOwnerTime < time && EXPOwnerTime > getEnvir()->getTime())
        {
            time = EXPOwnerTime;
        }

        if (SearchTime < time && SearchTime > getEnvir()->getTime())
        {
            time = SearchTime;
        }

        if (RoamTime < time && RoamTime > getEnvir()->getTime())
        {
            time = RoamTime;
        }

        if (ShockTime < time && ShockTime > getEnvir()->getTime())
        {
            time = ShockTime;
        }

        if (RegenTime < time && RegenTime > getEnvir()->getTime() && getHealth() < getMaxHealth())
        {
            time = RegenTime;
        }

        if (RageTime < time && RageTime > getEnvir()->getTime())
        {
            time = RageTime;
        }

        if (HallucinationTime < time && HallucinationTime > getEnvir()->getTime())
        {
            time = HallucinationTime;
        }

        if (ActionTime < time && ActionTime > getEnvir()->getTime())
        {
            time = ActionTime;
        }

        if (MoveTime < time && MoveTime > getEnvir()->getTime())
        {
            time = MoveTime;
        }

        if (AttackTime < time && AttackTime > getEnvir()->getTime())
        {
            time = AttackTime;
        }

        if (HealTime < time && HealTime > getEnvir()->getTime() && HealAmount > 0)
        {
            time = HealTime;
        }

        if (BrownTime < time && BrownTime > getEnvir()->getTime())
        {
            time = BrownTime;
        }

        for (int i = 0; i < ActionList.size(); i++)
        {
            if (ActionList[i]->Time >= time && ActionList[i]->Time > getEnvir()->getTime())
            {
                continue;
            }
            time = ActionList[i]->Time;
        }

        for (int i = 0; i < PoisonList.size(); i++)
        {
            if (PoisonList[i]->TickTime >= time && PoisonList[i]->TickTime > getEnvir()->getTime())
            {
                continue;
            }
            time = PoisonList[i]->TickTime;
        }

        for (int i = 0; i < Buffs.size(); i++)
        {
            if (Buffs[i]->ExpireTime >= time && Buffs[i]->ExpireTime > getEnvir()->getTime())
            {
                continue;
            }
            time = Buffs[i]->ExpireTime;
        }


        if (OperateTime <= getEnvir()->getTime() || time < OperateTime)
        {
            OperateTime = time;
        }
    }

    void MonsterObject::Process(DelayedAction *action)
    {
        switch (action->Type)
        {
            case DelayedType::Damage:
                CompleteAttack(action->Params);
                break;
            case DelayedType::RangeDamage:
                CompleteRangeAttack(action->Params);
                break;
            case DelayedType::Die:
                CompleteDeath(action->Params);
                break;
            case DelayedType::Recall:
                PetRecall();
                break;
        }
    }

    void MonsterObject::PetRecall()
    {
        if (Master == nullptr)
        {
            return;
        }
        if (!Teleport(Master->getCurrentMap(), Master->getBack()))
        {
            Teleport(Master->getCurrentMap(), Master->getCurrentLocation());
        }
    }

    void MonsterObject::CompleteAttack(std::vector<std::any> &data)
    {
        MapObject *target = std::any_cast<MapObject*>(data[0]);
        int damage = std::any_cast<int>(data[1]);
        DefenceType defence = std::any_cast<DefenceType>(data[2]);

        if (target == nullptr || !target->IsAttackTarget(this) || target->getCurrentMap() != getCurrentMap() || target->Node == nullptr)
        {
            return;
        }

        target->Attacked(this, damage, defence);
    }

    void MonsterObject::CompleteRangeAttack(std::vector<std::any> &data)
    {
        MapObject *target = std::any_cast<MapObject*>(data[0]);
        int damage = std::any_cast<int>(data[1]);
        DefenceType defence = std::any_cast<DefenceType>(data[2]);

        if (target == nullptr || !target->IsAttackTarget(this) || target->getCurrentMap() != getCurrentMap() || target->Node == nullptr)
        {
            return;
        }

        target->Attacked(this, damage, defence);
    }

    void MonsterObject::CompleteDeath(std::vector<std::any> &data)
    {
        throw NotImplementedException();
    }

    void MonsterObject::ProcessRegen()
    {
        if (Dead)
        {
            return;
        }

        int healthRegen = 0;

        if (getCanRegen())
        {
            RegenTime = getEnvir()->getTime() + RegenDelay;


            if (HP < Stats[Stat::HP])
            {
                healthRegen += static_cast<int>(Stats[Stat::HP] * 0.022F) + 1;
            }
        }


        if (getEnvir()->getTime() > HealTime)
        {
            HealTime = getEnvir()->getTime() + HealDelay;

            if (HealAmount > 5)
            {
                healthRegen += 5;
                HealAmount -= 5;
            }
            else
            {
                healthRegen += HealAmount;
                HealAmount = 0;
            }
        }

        if (healthRegen > 0)
        {
            ChangeHP(healthRegen);
        }
        if (HP == Stats[Stat::HP])
        {
            HealAmount = 0;
        }
    }

    void MonsterObject::ProcessPoison()
    {
        PoisonType type = PoisonType::None;
        ArmourRate = 1.0F;
        DamageRate = 1.0F;

        for (int i = PoisonList.size() - 1; i >= 0; i--)
        {
            if (Dead)
            {
                return;
            }

            Poison *poison = PoisonList[i];
            if (poison->Owner != nullptr && poison->Owner->Node == nullptr)
            {
                if (poison->PType == PoisonType::Slow)
                {
                    MoveSpeed = Info->MoveSpeed;
                    AttackSpeed = Info->AttackSpeed;
                    AttackTime = getEnvir()->getTime() + AttackSpeed;
                }
                PoisonList.erase(PoisonList.begin() + i);
                continue;
            }

            if (getEnvir()->getTime() > poison->TickTime)
            {
                poison->Time++;
                poison->TickTime = getEnvir()->getTime() + poison->TickSpeed;

                if (poison->Time >= poison->Duration)
                {
                    if (poison->PType == PoisonType::Slow)
                    {
                        MoveSpeed = Info->MoveSpeed;
                        AttackSpeed = Info->AttackSpeed;
                        AttackTime = getEnvir()->getTime() + AttackSpeed;
                    }
                    PoisonList.erase(PoisonList.begin() + i);
                    continue;
                }

                if (poison->PType == PoisonType::Green || poison->PType == PoisonType::Bleeding)
                {
                    if (EXPOwner == nullptr || EXPOwner->Dead)
                    {
                        EXPOwner = poison->Owner;
                        EXPOwnerTime = getEnvir()->getTime() + EXPOwnerDelay;
                    }
                    else if (EXPOwner == poison->Owner)
                    {
                        EXPOwnerTime = getEnvir()->getTime() + EXPOwnerDelay;
                    }

                    if (poison->PType == PoisonType::Bleeding)
                    {
                        S::ObjectEffect *tempVar = new S::ObjectEffect();
                        tempVar->ObjectID = ObjectID;
                        tempVar->Effect = SpellEffect::Bleeding;
                        tempVar->EffectType = 0;
                        Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
                    }

                    //ChangeHP(-poison.Value);
                    PoisonDamage(-poison->Value, poison->Owner);
                    if (PoisonStopRegen)
                    {
                        RegenTime = getEnvir()->getTime() + RegenDelay;
                    }
                }

                if (poison->PType == PoisonType::DelayedExplosion)
                {
                    if (getEnvir()->getTime() > ExplosionInflictedTime)
                    {
                        ExplosionInflictedStage++;
                    }

                    if (!ProcessDelayedExplosion(poison))
                    {
                        ExplosionInflictedStage = 0;
                        ExplosionInflictedTime = 0;

                        if (Dead)
                        {
                            break; //temp to stop crashing
                        }

                        PoisonList.erase(PoisonList.begin() + i);
                        continue;
                    }
                }
            }

            switch (poison->PType)
            {
                case PoisonType::Red:
                    ArmourRate -= 0.5F;
                    break;
                case PoisonType::Stun:
                    DamageRate += 0.5F;
                    break;
                case PoisonType::Blindness:
                    break;
                case PoisonType::Slow:
                    MoveSpeed = static_cast<unsigned short>(std::min(3500, MoveSpeed + 100));
                    AttackSpeed = static_cast<unsigned short>(std::min(3500, AttackSpeed + 100));

                    if (poison->Time >= poison->Duration)
                    {
                        MoveSpeed = Info->MoveSpeed;
                        AttackSpeed = Info->AttackSpeed;
                        //Reset the Attack time
                        AttackTime = getEnvir()->getTime() + AttackSpeed;
                    }
                    break;
            }
            type |= poison->PType;
            /*
            if ((int)type < (int)poison.PType)
                type = poison.PType;
             */
        }


        if (type == CurrentPoison)
        {
            return;
        }

        CurrentPoison = type;
        S::ObjectPoisoned *tempVar2 = new S::ObjectPoisoned();
        tempVar2->ObjectID = ObjectID;
        tempVar2->Poison = type;
        Broadcast(tempVar2);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
    }

    bool MonsterObject::ProcessDelayedExplosion(Poison *poison)
    {
        if (Dead)
        {
            return false;
        }

        if (ExplosionInflictedStage == 0)
        {
            S::ObjectEffect *tempVar = new S::ObjectEffect();
            tempVar->ObjectID = ObjectID;
            tempVar->Effect = SpellEffect::DelayedExplosion;
            tempVar->EffectType = 0;
            Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            return true;
        }
        if (ExplosionInflictedStage == 1)
        {
            if (getEnvir()->getTime() > ExplosionInflictedTime)
            {
                ExplosionInflictedTime = poison->TickTime + 3000;
            }
            S::ObjectEffect *tempVar2 = new S::ObjectEffect();
            tempVar2->ObjectID = ObjectID;
            tempVar2->Effect = SpellEffect::DelayedExplosion;
            tempVar2->EffectType = 1;
            Broadcast(tempVar2);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
            return true;
        }
        if (ExplosionInflictedStage == 2)
        {
            S::ObjectEffect *tempVar3 = new S::ObjectEffect();
            tempVar3->ObjectID = ObjectID;
            tempVar3->Effect = SpellEffect::DelayedExplosion;
            tempVar3->EffectType = 2;
            Broadcast(tempVar3);
            if (poison->Owner != nullptr)
            {
                switch (poison->Owner->getRace())
                {
                    case ObjectType::Player:
                    {
                        PlayerObject *caster = static_cast<PlayerObject*>(poison->Owner);
                        DelayedAction *action = new DelayedAction(DelayedType::Magic, getEnvir()->getTime(), {poison->Owner, caster->GetMagic(Spell::DelayedExplosion), poison->Value, this->getCurrentLocation()});
                        getCurrentMap()->ActionList.push_back(action);
                        //Attacked((PlayerObject)poison.Owner, poison.Value, DefenceType.MAC, false);
                        break;

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
                    }
                    case ObjectType::Monster: //this is in place so it could be used by mobs if one day someone chooses to
                        Attacked(static_cast<MonsterObject*>(poison->Owner), poison->Value, DefenceType::MAC);
                        break;
                }
                LastHitter = poison->Owner;
            }

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar3' statement was not added since tempVar3 was passed to a method or constructor. Handle memory management manually.
            return false;
        }
        return false;
    }

    void MonsterObject::ProcessBuffs()
    {
        bool refresh = false;
        for (int i = Buffs.size() - 1; i >= 0; i--)
        {
            Buff *buff = Buffs[i];

            if (getEnvir()->getTime() <= buff->ExpireTime || buff->Infinite)
            {
                continue;
            }

            Buffs.erase(Buffs.begin() + i);

            if (buff->Visible)
            {
                S::RemoveBuff *tempVar = new S::RemoveBuff();
                tempVar->Type = buff->Type;
                tempVar->ObjectID = ObjectID;
                Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            }

            switch (buff->Type)
            {
                case BuffType::Hiding:
                case BuffType::MoonLight:
                case BuffType::DarkBody:
                    if (!HasAnyBuffs(buff->Type, {BuffType::ClearRing, BuffType::Hiding, BuffType::MoonLight, BuffType::DarkBody}))
                    {
                        setHidden(false);
                    }
                    if (buff->Type == BuffType::MoonLight || buff->Type == BuffType::DarkBody)
                    {
                        if (!HasAnyBuffs(buff->Type, {BuffType::MoonLight, BuffType::DarkBody}))
                        {
                            setSneaking(false);
                        }
                        break;
                    }
                    break;
            }

            refresh = true;
        }

        if (refresh)
        {
            RefreshAll();
        }
    }

    void MonsterObject::ProcessAI()
    {
        if (Dead)
        {
            return;
        }

        if (Master != nullptr)
        {
            if ((Master->getPMode() == PetMode::Both || Master->getPMode() == PetMode::MoveOnly))
            {
                if (!Functions::InRange(getCurrentLocation(), Master->getCurrentLocation(), Globals::DataRange) || getCurrentMap() != Master->getCurrentMap())
                {
                    PetRecall();
                }
            }

            if (Master->getPMode() == PetMode::MoveOnly || Master->getPMode() == PetMode::None)
            {
                setTarget(nullptr);
            }
        }

        CheckAlone();

        if (!Alone || Settings::MonsterProcessWhenAlone)
        {
            ProcessStacking();

            if (!Stacking || Settings::MonsterProcessWhenStacked)
            {
                ProcessSearch();
                ProcessRoam();
                ProcessTarget();
            }
        }
    }

    void MonsterObject::CheckAlone()
    {
        if (getEnvir()->getTime() < AloneTime)
        {
            return;
        }

        AloneTime = getEnvir()->getTime() + AloneDelay;

        if (getCurrentMap()->Players.empty())
        {
            Alone = true;
            return;
        }

        for (int i = 0; i < getCurrentMap()->Players.size(); i++)
        {
            if (Functions::InRange(getCurrentLocation(), getCurrentMap()->Players[i]->getCurrentLocation(), Globals::DataRange * 2))
            {
                Alone = false;
                return;
            }
        }

        Alone = true;
    }

    void MonsterObject::ProcessStacking()
    {
        //Stacking or Infront of master - Move
        Stacking = CheckStacked();

        if (getCanMove() && ((Master != nullptr && Master->getFront() == getCurrentLocation()) || Stacking))
        {
            //Walk Randomly
            if (!Walk(getDirection()))
            {
                MirDirection dir = getDirection();

                switch (getEnvir()->Random->Next(3)) // favour Clockwise
                {
                    case 0:
                        for (int i = 0; i < 7; i++)
                        {
                            dir = Functions::NextDir(dir);

                            if (Walk(dir))
                            {
                                break;
                            }
                        }
                        break;
                    default:
                        for (int i = 0; i < 7; i++)
                        {
                            dir = Functions::PreviousDir(dir);

                            if (Walk(dir))
                            {
                                break;
                            }
                        }
                        break;
                }
            }

            return;
        }
    }

    void MonsterObject::ProcessSearch()
    {
        if (getEnvir()->getTime() < SearchTime)
        {
            return;
        }
        if (Master != nullptr && (Master->getPMode() == PetMode::MoveOnly || Master->getPMode() == PetMode::None))
        {
            return;
        }

        SearchTime = getEnvir()->getTime() + SearchDelay;

        if (getTarget() == nullptr || getEnvir()->Random->Next(3) == 0)
        {
            FindTarget();
        }
    }

    void MonsterObject::ProcessRoam()
    {
        if (getTarget() != nullptr || getEnvir()->getTime() < RoamTime)
        {
            return;
        }

        if (ProcessRoute())
        {
            return;
        }

        if (Master != nullptr)
        {
            MoveTo(Master->getBack());
            return;
        }

        RoamTime = getEnvir()->getTime() + RoamDelay;

        if (getEnvir()->Random->Next(10) != 0)
        {
            return;
        }

        switch (getEnvir()->Random->Next(3)) //Face Walk
        {
            case 0:
                Turn(static_cast<MirDirection>(getEnvir()->Random->Next(8)));
                break;
            default:
                Walk(getDirection());
                break;
        }
    }

    void MonsterObject::ProcessTarget()
    {
        if (getTarget() == nullptr || !getCanAttack())
        {
            return;
        }

        if (InAttackRange())
        {
            Attack();

            if (getTarget() != nullptr && getTarget()->Dead)
            {
                FindTarget();
            }

            return;
        }

        if (getEnvir()->getTime() < ShockTime)
        {
            setTarget(nullptr);
            return;
        }

        MoveTo(getTarget()->getCurrentLocation());
    }

    bool MonsterObject::InAttackRange()
    {
        if (getTarget()->getCurrentMap() != getCurrentMap())
        {
            return false;
        }

        return getTarget()->getCurrentLocation() != getCurrentLocation() && Functions::InRange(getCurrentLocation(), getTarget()->getCurrentLocation(), 1);
    }

    void MonsterObject::FindTarget()
    {
        Map *Current = getCurrentMap();

        for (int d = 0; d <= Info->ViewRange; d++)
        {
            for (int y = getCurrentLocation()->Y - d; y <= getCurrentLocation()->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= Current->Height)
                {
                    break;
                }

                for (int x = getCurrentLocation()->X - d; x <= getCurrentLocation()->X + d; x += std::abs(y - getCurrentLocation()->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= Current->Width)
                    {
                        break;
                    }
                    Cell *cell = Current->Cells[x][y];
                    if (cell->Objects.empty() || !cell->getValid())
                    {
                        continue;
                    }
                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        switch (ob->getRace())
                        {
                            case ObjectType::Monster:
                                if (!ob->IsAttackTarget(this))
                                {
                                    continue;
                                }
                                if (ob->getHidden() && (!CoolEye || getLevel() < ob->getLevel()))
                                {
                                    continue;
                                }
                                if (dynamic_cast<TrapRock*>(this) != nullptr && ob->getInTrapRock())
                                {
                                    continue;
                                }
                                setTarget(ob);
                                return;
                            case ObjectType::Player:
                            {
                                PlayerObject *playerob = static_cast<PlayerObject*>(ob);
                                if (!ob->IsAttackTarget(this))
                                {
                                    continue;
                                }
                                if (playerob->GMGameMaster || ob->getHidden() && (!CoolEye || getLevel() < ob->getLevel()) || getEnvir()->getTime() < HallucinationTime)
                                {
                                    continue;
                                }

                                setTarget(ob);

                                if (Master != nullptr)
                                {
                                    for (int j = 0; j < playerob->Pets.size(); j++)
                                    {
                                        MonsterObject *pet = playerob->Pets[j];

                                        if (!pet->IsAttackTarget(this))
                                        {
                                            continue;
                                        }
                                        setTarget(pet);
                                        break;
                                    }
                                }
                                return;
                            }
                            default:
                                continue;
                        }
                    }
                }
            }
        }
    }

    bool MonsterObject::ProcessRoute()
    {
        if (Route.size() < 1)
        {
            return false;
        }

        RoamTime = getEnvir()->getTime() + 500;

        if (getCurrentLocation() == Route[RoutePoint]->Location)
        {
            if (Route[RoutePoint]->Delay > 0 && !Waiting)
            {
                Waiting = true;
                RoamTime = getEnvir()->getTime() + RoamDelay + Route[RoutePoint]->Delay;
                return true;
            }

            Waiting = false;
            RoutePoint++;
        }

        if (RoutePoint > Route.size() - 1)
        {
            RoutePoint = 0;
        }

        if (!getCurrentMap()->ValidPoint(Route[RoutePoint]->Location))
        {
            return true;
        }

        MoveTo(Route[RoutePoint]->Location);

        return true;
    }

    void MonsterObject::MoveTo(Point *location)
    {
        if (getCurrentLocation() == location)
        {
            return;
        }

        bool inRange = Functions::InRange(location, getCurrentLocation(), 1);

        if (inRange)
        {
            if (!getCurrentMap()->ValidPoint(location))
            {
                return;
            }
            Cell *cell = getCurrentMap()->GetCell(location);
            if (!cell->Objects.empty())
            {
                for (int i = 0; i < cell->Objects.size(); i++)
                {
                    MapObject *ob = cell->Objects[i];
                    if (!ob->getBlocking())
                    {
                        continue;
                    }
                    return;
                }
            }
        }

        MirDirection dir = Functions::DirectionFromPoint(getCurrentLocation(), location);

        if (Walk(dir))
        {
            return;
        }

        switch (getEnvir()->Random->Next(2)) //No favour
        {
            case 0:
                for (int i = 0; i < 7; i++)
                {
                    dir = Functions::NextDir(dir);

                    if (Walk(dir))
                    {
                        return;
                    }
                }
                break;
            default:
                for (int i = 0; i < 7; i++)
                {
                    dir = Functions::PreviousDir(dir);

                    if (Walk(dir))
                    {
                        return;
                    }
                }
                break;
        }
    }

    void MonsterObject::Turn(MirDirection dir)
    {
        if (!getCanMove())
        {
            return;
        }

        setDirection(dir);

        InSafeZone = getCurrentMap()->GetSafeZone(getCurrentLocation()) != nullptr;

        Cell *cell = getCurrentMap()->GetCell(getCurrentLocation());

        for (int i = 0; i < cell->Objects.size(); i++)
        {
            if (cell->Objects[i]->getRace() != ObjectType::Spell)
            {
                continue;
            }
            SpellObject *ob = static_cast<SpellObject*>(cell->Objects[i]);

            ob->ProcessSpell(this);
            //break;
        }

        S::ObjectTurn *tempVar = new S::ObjectTurn();
        tempVar->ObjectID = ObjectID;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    bool MonsterObject::Walk(MirDirection dir)
    {
        if (!getCanMove())
        {
            return false;
        }

        Point *location = Functions::PointMove(getCurrentLocation(), dir, 1);

        if (!getCurrentMap()->ValidPoint(location))
        {
            return false;
        }

        Cell *cell = getCurrentMap()->GetCell(location);

        if (!cell->Objects.empty())
        {
            for (int i = 0; i < cell->Objects.size(); i++)
            {
                MapObject *ob = cell->Objects[i];
                if (!ob->getBlocking() || getRace() == ObjectType::Creature)
                {
                    continue;
                }

                return false;
            }
        }

        getCurrentMap()->GetCell(getCurrentLocation())->Remove(this);

        setDirection(dir);
        RemoveObjects(dir, 1);
        setCurrentLocation(location);
        getCurrentMap()->GetCell(getCurrentLocation())->Add(this);
        AddObjects(dir, 1);

        if (getHidden())
        {
            RemoveBuff(BuffType::Hiding);
        }

        CellTime = getEnvir()->getTime() + 500;
        ActionTime = getEnvir()->getTime() + 300;
        MoveTime = getEnvir()->getTime() + MoveSpeed;
        if (MoveTime > AttackTime)
        {
            AttackTime = MoveTime;
        }

        InSafeZone = getCurrentMap()->GetSafeZone(getCurrentLocation()) != nullptr;

        S::ObjectWalk *tempVar = new S::ObjectWalk();
        tempVar->ObjectID = ObjectID;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        Broadcast(tempVar);


        cell = getCurrentMap()->GetCell(getCurrentLocation());

        for (int i = 0; i < cell->Objects.size(); i++)
        {
            if (cell->Objects[i]->getRace() != ObjectType::Spell)
            {
                continue;
            }
            SpellObject *ob = static_cast<SpellObject*>(cell->Objects[i]);

            ob->ProcessSpell(this);
            //break;
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        return true;
    }

    void MonsterObject::Attack()
    {
        if (BindingShotCenter)
        {
            ReleaseBindingShot();
        }

        ShockTime = 0;

        if (!getTarget()->IsAttackTarget(this))
        {
            setTarget(nullptr);
            return;
        }

        setDirection(Functions::DirectionFromPoint(getCurrentLocation(), getTarget()->getCurrentLocation()));
        S::ObjectAttack *tempVar = new S::ObjectAttack();
        tempVar->ObjectID = ObjectID;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        Broadcast(tempVar);

        ActionTime = getEnvir()->getTime() + 300;
        AttackTime = getEnvir()->getTime() + AttackSpeed;

        int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
        if (damage == 0)
        {
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            return;
        }

        DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + 300, {getTarget(), damage, DefenceType::ACAgility});
        ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    void MonsterObject::ReleaseBindingShot()
    {
        if (!BindingShotCenter)
        {
            return;
        }

        ShockTime = 0;
        Broadcast(GetInfo()); //update clients in range (remove effect)
        BindingShotCenter = false;

        //the centertarget is escaped so make all shocked mobs awake (3x3 from center)
        Point *place = getCurrentLocation();
        for (int y = place->Y - 1; y <= place->Y + 1; y++)
        {
            if (y < 0)
            {
                continue;
            }
            if (y >= getCurrentMap()->Height)
            {
                break;
            }

            for (int x = place->X - 1; x <= place->X + 1; x++)
            {
                if (x < 0)
                {
                    continue;
                }
                if (x >= getCurrentMap()->Width)
                {
                    break;
                }

                Cell *cell = getCurrentMap()->GetCell(x, y);
                if (!cell->getValid() || cell->Objects.empty())
                {
                    continue;
                }

                for (int i = 0; i < cell->Objects.size(); i++)
                {
                    MapObject *targetob = cell->Objects[i];
                    if (targetob == nullptr || targetob->Node == nullptr || targetob->getRace() != ObjectType::Monster)
                    {
                        continue;
                    }
                    if ((static_cast<MonsterObject*>(targetob))->ShockTime == 0)
                    {
                        continue;
                    }

                    //each centerTarget has its own effect which needs to be cleared when no longer shocked
                    if ((static_cast<MonsterObject*>(targetob))->BindingShotCenter)
                    {
                        (static_cast<MonsterObject*>(targetob))->ReleaseBindingShot();
                    }
                    else
                    {
                        (static_cast<MonsterObject*>(targetob))->ShockTime = 0;
                    }

                    break;
                }
            }
        }
    }

    bool MonsterObject::FindNearby(int distance)
    {
        for (int d = 0; d <= distance; d++)
        {
            for (int y = getCurrentLocation()->Y - d; y <= getCurrentLocation()->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = getCurrentLocation()->X - d; x <= getCurrentLocation()->X + d; x += std::abs(y - getCurrentLocation()->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }
                    if (!getCurrentMap()->ValidPoint(x, y))
                    {
                        continue;
                    }
                    Cell *cell = getCurrentMap()->GetCell(x, y);
                    if (cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        switch (ob->getRace())
                        {
                            case ObjectType::Monster:
                            case ObjectType::Player:
                                if (!ob->IsAttackTarget(this))
                                {
                                    continue;
                                }
                                if (ob->getHidden() && (!CoolEye || getLevel() < ob->getLevel()))
                                {
                                    continue;
                                }
                                if (ob->getRace() == ObjectType::Player)
                                {
                                    PlayerObject *player = (static_cast<PlayerObject*>(ob));
                                    if (player->GMGameMaster)
                                    {
                                        continue;
                                    }
                                }
                                return true;
                            default:
                                continue;
                        }
                    }
                }
            }
        }

        return false;
    }

    bool MonsterObject::FindFriendsNearby(int distance)
    {
        for (int d = 0; d <= distance; d++)
        {
            for (int y = getCurrentLocation()->Y - d; y <= getCurrentLocation()->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = getCurrentLocation()->X - d; x <= getCurrentLocation()->X + d; x += std::abs(y - getCurrentLocation()->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }
                    if (!getCurrentMap()->ValidPoint(x, y))
                    {
                        continue;
                    }
                    Cell *cell = getCurrentMap()->GetCell(x, y);
                    if (cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        switch (ob->getRace())
                        {
                            case ObjectType::Monster:
                            case ObjectType::Player:
                                if (ob == this || ob->Dead)
                                {
                                    continue;
                                }
                                if (ob->IsAttackTarget(this))
                                {
                                    continue;
                                }
                                if (ob->getRace() == ObjectType::Player)
                                {
                                    PlayerObject *player = (static_cast<PlayerObject*>(ob));
                                    if (player->GMGameMaster)
                                    {
                                        continue;
                                    }
                                }
                                return true;
                            default:
                                continue;
                        }
                    }
                }
            }
        }

        return false;
    }

    std::vector<MapObject*> MonsterObject::FindAllNearby(int dist, Point *location, bool needSight)
    {
        std::vector<MapObject*> targets;
        for (int d = 0; d <= dist; d++)
        {
            for (int y = location->Y - d; y <= location->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = location->X - d; x <= location->X + d; x += std::abs(y - location->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }

                    Cell *cell = getCurrentMap()->GetCell(x, y);
                    if (!cell->getValid() || cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        switch (ob->getRace())
                        {
                            case ObjectType::Monster:
                            case ObjectType::Player:
                                targets.push_back(ob);
                                continue;
                            default:
                                continue;
                        }
                    }
                }
            }
        }
        return targets;
    }

    std::vector<MapObject*> MonsterObject::FindAllTargets(int dist, Point *location, bool needSight)
    {
        std::vector<MapObject*> targets;
        for (int d = 0; d <= dist; d++)
        {
            for (int y = location->Y - d; y <= location->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = location->X - d; x <= location->X + d; x += std::abs(y - location->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }

                    Cell *cell = getCurrentMap()->GetCell(x, y);
                    if (!cell->getValid() || cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        switch (ob->getRace())
                        {
                            case ObjectType::Monster:
                            case ObjectType::Player:
                                if (!ob->IsAttackTarget(this))
                                {
                                    continue;
                                }
                                if (ob->getHidden() && (!CoolEye || getLevel() < ob->getLevel()) && needSight)
                                {
                                    continue;
                                }
                                if (ob->getRace() == ObjectType::Player)
                                {
                                    PlayerObject *player = (static_cast<PlayerObject*>(ob));
                                    if (player->GMGameMaster)
                                    {
                                        continue;
                                    }
                                }
                                targets.push_back(ob);
                                continue;
                            default:
                                continue;
                        }
                    }
                }
            }
        }
        return targets;
    }

    bool MonsterObject::IsAttackTarget(PlayerObject *attacker)
    {
        if (attacker == nullptr || attacker->Node == nullptr)
        {
            return false;
        }
        if (Dead)
        {
            return false;
        }
        if (Master == nullptr)
        {
            return true;
        }
        if (attacker->getAMode() == AttackMode::Peace)
        {
            return false;
        }
        if (Master == attacker)
        {
            return attacker->getAMode() == AttackMode::All;
        }
        if (Master->getRace() == ObjectType::Player && (attacker->InSafeZone || InSafeZone))
        {
            return false;
        }

        switch (attacker->getAMode())
        {
            case AttackMode::Group:
                return Master->GroupMembers.empty() || !std::find(Master->GroupMembers.begin(), Master->GroupMembers.end(), attacker) != Master->GroupMembers.end();
            case AttackMode::Guild:
            {
                    if (!(dynamic_cast<PlayerObject*>(Master) != nullptr))
                    {
                        return false;
                    }
                    PlayerObject *master = static_cast<PlayerObject*>(Master);
                    return master->MyGuild == nullptr || master->MyGuild != attacker->MyGuild;
            }
            case AttackMode::EnemyGuild:
            {
                    if (!(dynamic_cast<PlayerObject*>(Master) != nullptr))
                    {
                        return false;
                    }
                    PlayerObject *master = static_cast<PlayerObject*>(Master);
                    return (master->MyGuild != nullptr && attacker->MyGuild != nullptr) && master->MyGuild->IsEnemy(attacker->MyGuild);
            }
            case AttackMode::RedBrown:
                return Master->getPKPoints() >= 200 || getEnvir()->getTime() < Master->BrownTime;
            default:
                return true;
        }
    }

    bool MonsterObject::IsAttackTarget(MonsterObject *attacker)
    {
        if (attacker == nullptr || attacker->Node == nullptr)
        {
            return false;
        }
        if (Dead || attacker == this)
        {
            return false;
        }
        if (attacker->getRace() == ObjectType::Creature)
        {
            return false;
        }

        if (attacker->Info->AI == 6) // Guard
        {
            if (Info->AI != 1 && Info->AI != 2 && Info->AI != 3 && (Master == nullptr || Master->getPKPoints() >= 200)) //Not Dear/Hen/Tree/Pets or Red Master
            {
                return true;
            }
        }
        else if (attacker->Info->AI == 58) // Tao Guard - attacks Pets
        {
            if (Info->AI != 1 && Info->AI != 2 && Info->AI != 3 && (Master == nullptr || Master->getAMode() != AttackMode::Peace)) //Not Dear/Hen/Tree or Peaceful Master
            {
                return true;
            }
        }
        else if (Master != nullptr) //Pet Attacked
        {
            if (attacker->Master == nullptr) //Wild Monster
            {
                return true;
            }

            //Pet Vs Pet
            if (Master == attacker->Master)
            {
                return false;
            }

            if (getEnvir()->getTime() < ShockTime) //Shocked
            {
                return false;
            }

            if (Master->getRace() == ObjectType::Player && attacker->Master->getRace() == ObjectType::Player && (Master->InSafeZone || attacker->Master->InSafeZone))
            {
                return false;
            }

            switch (attacker->Master->getAMode())
            {
                case AttackMode::Group:
                    if (!Master->GroupMembers.empty() && std::find(Master->GroupMembers.begin(), Master->GroupMembers.end(), static_cast<PlayerObject*>(attacker->Master) != Master->GroupMembers.end()))
                    {
                        return false;
                    }
                    break;
                case AttackMode::Guild:
                    break;
                case AttackMode::EnemyGuild:
                    break;
                case AttackMode::RedBrown:
                    if (attacker->Master->getPKPoints() < 200 || getEnvir()->getTime() > attacker->Master->BrownTime)
                    {
                        return false;
                    }
                    break;
                case AttackMode::Peace:
                    return false;
            }

            for (int i = 0; i < Master->Pets.size(); i++)
            {
                if (Master->Pets[i]->EXPOwner == attacker->Master)
                {
                    return true;
                }
            }

            for (int i = 0; i < attacker->Master->Pets.size(); i++)
            {
                MonsterObject *ob = attacker->Master->Pets[i];
                if (ob == getTarget() || ob->getTarget() == this)
                {
                    return true;
                }
            }

            return Master->LastHitter == attacker->Master;
        }
        else if (attacker->Master != nullptr) //Pet Attacking Wild Monster
        {
            if (getEnvir()->getTime() < ShockTime) //Shocked
            {
                return false;
            }

            for (int i = 0; i < attacker->Master->Pets.size(); i++)
            {
                MonsterObject *ob = attacker->Master->Pets[i];
                if (ob == getTarget() || ob->getTarget() == this)
                {
                    return true;
                }
            }

            if (getTarget() == attacker->Master)
            {
                return true;
            }
        }

        if (getEnvir()->getTime() < attacker->HallucinationTime)
        {
            return true;
        }

        return getEnvir()->getTime() < attacker->RageTime;
    }

    bool MonsterObject::IsFriendlyTarget(PlayerObject *ally)
    {
        if (Master == nullptr)
        {
            return false;
        }
        if (Master == ally)
        {
            return true;
        }

        switch (ally->getAMode())
        {
            case AttackMode::Group:
                return !Master->GroupMembers.empty() && std::find(Master->GroupMembers.begin(), Master->GroupMembers.end(), ally) != Master->GroupMembers.end();
            case AttackMode::Guild:
                return false;
            case AttackMode::EnemyGuild:
                return true;
            case AttackMode::RedBrown:
                return Master->PKPoints < 200 & getEnvir()->getTime() > Master->BrownTime;
        }
        return true;
    }

    bool MonsterObject::IsFriendlyTarget(MonsterObject *ally)
    {
        if (Master != nullptr)
        {
            return false;
        }
        if (ally->getRace() != ObjectType::Monster)
        {
            return false;
        }
        if (ally->Master != nullptr)
        {
            return false;
        }

        return true;
    }

    int MonsterObject::Attacked(PlayerObject *attacker, int damage, DefenceType type, bool damageWeapon)
    {
        if (getTarget() == nullptr && attacker->IsAttackTarget(this))
        {
            setTarget(attacker);
        }

        bool hit;
        auto armour = GetArmour(type, attacker, hit);

        if (!hit)
        {
            return 0;
        }

        armour = static_cast<int>(std::max(std::numeric_limits<int>::min(), (std::min(std::numeric_limits<int>::max(), static_cast<Decimal>(armour * ArmourRate)))));
        damage = static_cast<int>(std::max(std::numeric_limits<int>::min(), (std::min(std::numeric_limits<int>::max(), static_cast<Decimal>(damage * DamageRate)))));

        if (damageWeapon)
        {
            attacker->DamageWeapon();
        }
        damage += attacker->Stats[Stat::AttackBonus];

        if (armour >= damage)
        {
            BroadcastDamageIndicator(DamageType::Miss);
            return 0;
        }

        if (getEnvir()->Random->Next(100) < (attacker->Stats[Stat::CriticalRate] * Settings::CriticalRateWeight))
        {
            S::ObjectEffect *tempVar = new S::ObjectEffect();
            tempVar->ObjectID = ObjectID;
            tempVar->Effect = SpellEffect::Critical;
            Broadcast(tempVar);
            damage = std::min(std::numeric_limits<int>::max(), damage + static_cast<int>(std::floor(damage * ((static_cast<double>(attacker->Stats[Stat::CriticalDamage]) / static_cast<double>(Settings::CriticalDamageWeight)) * 10))));
            BroadcastDamageIndicator(DamageType::Critical);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }

        if (getTarget() != this && attacker->IsAttackTarget(this))
        {
            if (attacker->Info->MentalState == 2)
            {
                if (Functions::MaxDistance(getCurrentLocation(), attacker->getCurrentLocation()) < (8 - attacker->Info->MentalStateLvl))
                {
                    setTarget(attacker);
                }
            }
            else
            {
                setTarget(attacker);
            }
        }

        if (BindingShotCenter)
        {
            ReleaseBindingShot();
        }
        ShockTime = 0;

        for (int i = PoisonList.size() - 1; i >= 0; i--)
        {
            if (PoisonList[i]->PType != PoisonType::LRParalysis)
            {
                continue;
            }

            PoisonList.erase(PoisonList.begin() + i);
            OperateTime = 0;
        }

        if (Master != nullptr && Master != attacker)
        {
            if (getEnvir()->getTime() > Master->BrownTime && Master->getPKPoints() < 200)
            {
                attacker->BrownTime = getEnvir()->getTime() + Settings::Minute;
            }
        }

        if (EXPOwner == nullptr || EXPOwner->Dead)
        {
            EXPOwner = attacker;
        }

        if (EXPOwner == attacker)
        {
            EXPOwnerTime = getEnvir()->getTime() + EXPOwnerDelay;
        }

        unsigned short levelOffset = static_cast<unsigned short>(getLevel() > attacker->getLevel() ? 0 : std::min(10, attacker->getLevel() - getLevel()));

        ApplyNegativeEffects(attacker, type, levelOffset);

        S::ObjectStruck *tempVar2 = new S::ObjectStruck();
        tempVar2->ObjectID = ObjectID;
        tempVar2->AttackerID = attacker->ObjectID;
        tempVar2->Direction = getDirection();
        tempVar2->Location = getCurrentLocation();
        Broadcast(tempVar2);

        if (attacker->Stats[Stat::HPDrainRatePercent] > 0)
        {
            attacker->HpDrain += std::max(0, (static_cast<float>(damage - armour) / 100) * attacker->Stats[Stat::HPDrainRatePercent]);
            if (attacker->HpDrain > 2)
            {
                int hpGain = static_cast<int>(std::floor(attacker->HpDrain));
                attacker->ChangeHP(hpGain);
                attacker->HpDrain -= hpGain;
            }
        }

        attacker->GatherElement();

        if (attacker->Info->Mentor != 0 && attacker->Info->isMentor)
        {
            if (attacker->HasBuff(BuffType::Mentor, _))
            {
                CharacterInfo *Mentee = getEnvir()->GetCharacterInfo(attacker->Info->Mentor);
                PlayerObject *player = getEnvir()->GetPlayer(Mentee->Name);
                if (player->getCurrentMap() == attacker->getCurrentMap() && Functions::InRange(player->getCurrentLocation(), attacker->getCurrentLocation(), Globals::DataRange) && !player->Dead)
                {
                    damage += (damage * Stats[Stat::MentorDamageRatePercent]) / 100;
                }
            }
        }

        if (Master != nullptr && Master != attacker && Master->getRace() == ObjectType::Player && getEnvir()->getTime() > Master->BrownTime && Master->getPKPoints() < 200 && !(static_cast<PlayerObject*>(Master))->AtWar(attacker))
        {
            attacker->BrownTime = getEnvir()->getTime() + Settings::Minute;
        }

        for (int i = 0; i < attacker->Pets.size(); i++)
        {
            MonsterObject *ob = attacker->Pets[i];

            if (IsAttackTarget(ob) && (ob->getTarget() == nullptr))
            {
                ob->setTarget(this);
            }
        }

        BroadcastDamageIndicator(DamageType::Hit, armour - damage);

        ChangeHP(armour - damage);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
        return damage - armour;
    }

    int MonsterObject::Attacked(MonsterObject *attacker, int damage, DefenceType type)
    {
        if (getTarget() == nullptr && attacker->IsAttackTarget(this))
        {
            setTarget(attacker);
        }


        bool hit;
        auto armour = GetArmour(type, attacker, hit);
        if (!hit)
        {
            return 0;
        }

        armour = static_cast<int>(std::max(std::numeric_limits<int>::min(), (std::min(std::numeric_limits<int>::max(), static_cast<Decimal>(armour * ArmourRate)))));
        damage = static_cast<int>(std::max(std::numeric_limits<int>::min(), (std::min(std::numeric_limits<int>::max(), static_cast<Decimal>(damage * DamageRate)))));

        if (armour >= damage)
        {
            BroadcastDamageIndicator(DamageType::Miss);
            return 0;
        }

        if (getTarget() != this && attacker->IsAttackTarget(this))
        {
            setTarget(attacker);
        }

        if (BindingShotCenter)
        {
            ReleaseBindingShot();
        }
        ShockTime = 0;

        for (int i = PoisonList.size() - 1; i >= 0; i--)
        {
            if (PoisonList[i]->PType != PoisonType::LRParalysis)
            {
                continue;
            }

            PoisonList.erase(PoisonList.begin() + i);
            OperateTime = 0;
        }

        if (attacker->Info->AI == 6 || attacker->Info->AI == 58)
        {
            EXPOwner = nullptr;
        }

        else if (attacker->Master != nullptr)
        {
            if (attacker->getCurrentMap() != attacker->Master->getCurrentMap() || !Functions::InRange(attacker->getCurrentLocation(), attacker->Master->getCurrentLocation(), Globals::DataRange))
            {
                EXPOwner = nullptr;
            }
            else
            {

                if (EXPOwner == nullptr || EXPOwner->Dead)
                {
                    EXPOwner = attacker->Master;
                }

                if (EXPOwner == attacker->Master)
                {
                    EXPOwnerTime = getEnvir()->getTime() + EXPOwnerDelay;
                }
            }

        }

        S::ObjectStruck *tempVar = new S::ObjectStruck();
        tempVar->ObjectID = ObjectID;
        tempVar->AttackerID = attacker->ObjectID;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        Broadcast(tempVar);

        BroadcastDamageIndicator(DamageType::Hit, armour - damage);

        ChangeHP(armour - damage);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        return damage - armour;
    }

    int MonsterObject::Struck(int damage, DefenceType type)
    {
        int armour = 0;

        switch (type)
        {
            case DefenceType::ACAgility:
                armour = GetAttackPower(Stats[Stat::MinAC], Stats[Stat::MaxAC]);
                break;
            case DefenceType::AC:
                armour = GetAttackPower(Stats[Stat::MinAC], Stats[Stat::MaxAC]);
                break;
            case DefenceType::MACAgility:
                armour = GetAttackPower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);
                break;
            case DefenceType::MAC:
                armour = GetAttackPower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);
                break;
            case DefenceType::Agility:
                break;
        }

        armour = static_cast<int>(std::max(std::numeric_limits<int>::min(), (std::min(std::numeric_limits<int>::max(), static_cast<Decimal>(armour * ArmourRate)))));
        damage = static_cast<int>(std::max(std::numeric_limits<int>::min(), (std::min(std::numeric_limits<int>::max(), static_cast<Decimal>(damage * DamageRate)))));

        if (armour >= damage)
        {
            return 0;
        }
        S::ObjectStruck *tempVar = new S::ObjectStruck();
        tempVar->ObjectID = ObjectID;
        tempVar->AttackerID = 0;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        Broadcast(tempVar);

        ChangeHP(armour - damage);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        return damage - armour;
    }

    void MonsterObject::ApplyPoison(Poison *p, MapObject *Caster, bool NoResist, bool ignoreDefence)
    {
        if (p->Owner != nullptr && p->Owner->IsAttackTarget(this))
        {
            setTarget(p->Owner);
        }

        if (Master != nullptr && p->Owner != nullptr && p->Owner->getRace() == ObjectType::Player && p->Owner != Master)
        {
            if (getEnvir()->getTime() > Master->BrownTime && Master->getPKPoints() < 200)
            {
                p->Owner->BrownTime = getEnvir()->getTime() + Settings::Minute;
            }
        }

        if (!ignoreDefence && (p->PType == PoisonType::Green))
        {
            int armour = GetAttackPower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);

            if (p->Value < armour)
            {
                p->PType = PoisonType::None;
            }
            else
            {
                p->Value -= armour;
            }
        }

        if (p->PType == PoisonType::None)
        {
            return;
        }

        for (int i = 0; i < PoisonList.size(); i++)
        {
            if (PoisonList[i]->PType != p->PType)
            {
                continue;
            }
            if ((PoisonList[i]->PType == PoisonType::Green) && (PoisonList[i]->Value > p->Value))
            {
                return; //cant cast weak poison to cancel out strong poison
            }
            if ((PoisonList[i]->PType != PoisonType::Green) && ((PoisonList[i]->Duration - PoisonList[i]->Time) > p->Duration))
            {
                return; //cant cast 1 second poison to make a 1minute poison go away!
            }
            if (p->PType == PoisonType::DelayedExplosion)
            {
                return;
            }
            if ((PoisonList[i]->PType == PoisonType::Frozen) || (PoisonList[i]->PType == PoisonType::Slow) || (PoisonList[i]->PType == PoisonType::Paralysis) || (PoisonList[i]->PType == PoisonType::LRParalysis))
            {
                return; //prevents mobs from being perma frozen/slowed
            }
            PoisonList[i] = p;
            return;
        }

        if (p->PType == PoisonType::DelayedExplosion)
        {
            ExplosionInflictedTime = getEnvir()->getTime() + 4000;
            S::ObjectEffect *tempVar = new S::ObjectEffect();
            tempVar->ObjectID = ObjectID;
            tempVar->Effect = SpellEffect::DelayedExplosion;
            Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }

        PoisonList.push_back(p);
    }

    Buff *MonsterObject::AddBuff(BuffType type, MapObject *owner, int duration, Stats *stats, bool visible, bool infinite, bool stackable, bool refreshStats, std::vector<int> &values)
    {
        Buff *b = MapObject::AddBuff(type, owner, duration, stats, visible, infinite, stackable, refreshStats, {values});

        switch (b->Type)
        {
            case BuffType::MonsterMACBuff:
                S::ObjectEffect *tempVar = new S::ObjectEffect(), *CurrentLocation;
                tempVar->ObjectID = ObjectID;
                tempVar->Effect = SpellEffect::MonsterMACBuff;
                getCurrentMap()->Broadcast(tempVar, CurrentLocation);
                break;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }

        if (HasBuff(type, b) && b->Infinite == true)
        {
            return b;
        }

        auto packet = new S::AddBuff();
        packet->Buff = b->ToClientBuff();

        packet->Buff->ExpireTime -= getEnvir()->getTime();

        if (b->Visible)
        {
            Broadcast(packet);
        }

        if (refreshStats)
        {
            RefreshAll();
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete packet' statement was not added since packet was passed to a method or constructor. Handle memory management manually.
        return b;
    }

    Packet *MonsterObject::GetInfo()
    {
        return new S::ObjectMonster { ObjectID = ObjectID, Name = getName(), NameColour = NameColour, Location = getCurrentLocation(), Image = Info->Image, Direction = getDirection(), Effect = Info->Effect, AI = Info->AI, Light = Info->Light, Dead = Dead, Skeleton = Harvested, Poison = CurrentPoison, Hidden = getHidden(), ShockTime = (ShockTime > 0 ? ShockTime - getEnvir()->getTime() : 0), BindingShotCenter = BindingShotCenter, Buffs = Buffs.Where([&] (std::any d)
        {
            return d->Visible;
        })->Select([&] (std::any e)
        {
            e::Type;
        }).ToList() };
    }

    void MonsterObject::ReceiveChat(const std::string &text, ChatType type)
    {
        throw NotSupportedException();
    }

    void MonsterObject::RemoveObjects(MirDirection dir, int count)
    {
        switch (dir)
        {
            case MirDirection::Up:
                //Bottom Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y + Globals::DataRange - a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::UpRight:
                //Bottom Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y + Globals::DataRange - a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }

                //Left Block
                for (int a = -Globals::DataRange; a <= Globals::DataRange - count; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X - Globals::DataRange + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::Right:
                //Left Block
                for (int a = -Globals::DataRange; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X - Globals::DataRange + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::DownRight:
                //Top Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y - Globals::DataRange + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }

                //Left Block
                for (int a = -Globals::DataRange + count; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X - Globals::DataRange + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::Down:
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y - Globals::DataRange + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::DownLeft:
                //Top Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y - Globals::DataRange + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }

                //Right Block
                for (int a = -Globals::DataRange + count; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X + Globals::DataRange - b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::Left:
                for (int a = -Globals::DataRange; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X + Globals::DataRange - b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
            case MirDirection::UpLeft:
                //Bottom Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y + Globals::DataRange - a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }

                //Right Block
                for (int a = -Globals::DataRange; a <= Globals::DataRange - count; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X + Globals::DataRange - b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Remove(this);
                        }
                    }
                }
                break;
        }
    }

    void MonsterObject::AddObjects(MirDirection dir, int count)
    {
        switch (dir)
        {
            case MirDirection::Up:
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y - Globals::DataRange + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::UpRight:
                //Top Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y - Globals::DataRange + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }

                //Right Block
                for (int a = -Globals::DataRange + count; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X + Globals::DataRange - b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::Right:
                for (int a = -Globals::DataRange; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X + Globals::DataRange - b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::DownRight:
                //Bottom Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y + Globals::DataRange - a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }

                //Right Block
                for (int a = -Globals::DataRange; a <= Globals::DataRange - count; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X + Globals::DataRange - b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::Down:
                //Bottom Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y + Globals::DataRange - a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::DownLeft:
                //Bottom Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y + Globals::DataRange - a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }

                //Left Block
                for (int a = -Globals::DataRange; a <= Globals::DataRange - count; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X - Globals::DataRange + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::Left:
                //Left Block
                for (int a = -Globals::DataRange; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X - Globals::DataRange + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
            case MirDirection::UpLeft:
                //Top Block
                for (int a = 0; a < count; a++)
                {
                    int y = getCurrentLocation()->Y - Globals::DataRange + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = -Globals::DataRange; b <= Globals::DataRange; b++)
                    {
                        int x = getCurrentLocation()->X + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }

                //Left Block
                for (int a = -Globals::DataRange + count; a <= Globals::DataRange; a++)
                {
                    int y = getCurrentLocation()->Y + a;
                    if (y < 0 || y >= getCurrentMap()->Height)
                    {
                        continue;
                    }

                    for (int b = 0; b < count; b++)
                    {
                        int x = getCurrentLocation()->X - Globals::DataRange + b;
                        if (x < 0 || x >= getCurrentMap()->Width)
                        {
                            continue;
                        }

                        Cell *cell = getCurrentMap()->GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *ob = cell->Objects[i];
                            if (ob->getRace() != ObjectType::Player)
                            {
                                continue;
                            }
                            ob->Add(this);
                        }
                    }
                }
                break;
        }
    }

    void MonsterObject::Add(PlayerObject *player)
    {
        player->Enqueue(GetInfo());
        SendHealth(player);
    }

    void MonsterObject::SendHealth(PlayerObject *player)
    {
        if (!player->IsMember(Master) && !(player->IsMember(EXPOwner) && AutoRev) && getEnvir()->getTime() > RevTime)
        {
            return;
        }
        unsigned char time = std::min(std::numeric_limits<unsigned char>::max(), static_cast<unsigned char>(std::max(5, (RevTime - getEnvir()->getTime()) / 1000)));
        S::ObjectHealth *tempVar = new S::ObjectHealth();
        tempVar->ObjectID = ObjectID;
        tempVar->Percent = getPercentHealth();
        tempVar->Expire = time;
        player->Enqueue(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    void MonsterObject::PetExp(unsigned int amount)
    {
        if (PetLevel >= MaxPetLevel)
        {
            return;
        }

        if (Info->Name == Settings::SkeletonName || Info->Name == Settings::ShinsuName || Info->Name == Settings::AngelName)
        {
            amount *= 3;
        }

        PetExperience += amount;

        if (PetExperience < (PetLevel + 1) * 20000)
        {
            return;
        }

        PetExperience = static_cast<unsigned int>(PetExperience - ((PetLevel + 1) * 20000));
        PetLevel++;
        RefreshAll();
        OperateTime = 0;
        BroadcastHealthChange();
    }

    void MonsterObject::Despawn()
    {
        SlaveList.clear();
        MapObject::Despawn();
    }

    void MonsterObject::PoisonTarget(MapObject *target, int chanceToPoison, long long poisonDuration, PoisonType poison, long long poisonTickSpeed)
    {
        int value = GetAttackPower(Stats[Stat::MinSC], Stats[Stat::MaxSC]);

        if (getEnvir()->Random->Next(Settings::PoisonResistWeight) >= target->Stats[Stat::PoisonResist])
        {
            if (getEnvir()->Random->Next(chanceToPoison) == 0)
            {
                Poison *tempVar = new Poison(), *this;
                tempVar->Owner = this;
                tempVar->Duration = poisonDuration;
                tempVar->PType = poison;
                tempVar->Value = value;
                tempVar->TickSpeed = poisonTickSpeed;
                target->ApplyPoison(tempVar, this);

                if (poison == PoisonType::Dazed)
                {
                    S::ObjectEffect *tempVar2 = new S::ObjectEffect();
                    tempVar2->ObjectID = target->ObjectID;
                    tempVar2->Effect = SpellEffect::Stunned;
                    tempVar2->Time = static_cast<unsigned int>(poisonDuration * poisonTickSpeed);
                    Broadcast(tempVar2);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
                }

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            }
        }
    }

    void MonsterObject::TriangleAttack(int distance, int limitWidth, int additionalDelay, DefenceType defenceType, bool push)
    {
        int damage;

        switch (defenceType)
        {
            case DefenceType::MAC:
            case DefenceType::MACAgility:
                damage = GetAttackPower(Stats[Stat::MinMC], Stats[Stat::MaxMC]);
                break;
            default:
                damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
                break;
        }

        if (damage == 0)
        {
            return;
        }

        std::vector<Point*> points;

        for (int i = 1; i <= distance; i++)
        {
            Point *target = Functions::PointMove(getCurrentLocation(), getDirection(), i);

            if (!getCurrentMap()->ValidPoint(target))
            {
                continue;
            }

            points.push_back(target);

            if (distance > 1)
            {
                Point *left = target;
                Point *right = target;

                auto offset = i - 1;

                for (int l = 1; l <= offset; l++)
                {
                    if (limitWidth > -1 && l > limitWidth)
                    {
                        break;
                    }

                    left = Functions::Left(left, getDirection());
                    if (!getCurrentMap()->ValidPoint(left))
                    {
                        continue;
                    }
                    points.push_back(left);
                }

                for (int r = 1; r <= offset; r++)
                {
                    if (limitWidth > -1 && r > limitWidth)
                    {
                        break;
                    }

                    right = Functions::Right(right, getDirection());
                    if (!getCurrentMap()->ValidPoint(right))
                    {
                        continue;
                    }
                    points.push_back(right);
                }
            }
        }

        for (auto point : points)
        {
            Cell *cell = getCurrentMap()->GetCell(point);
            if (cell->Objects.empty())
            {
                continue;
            }

            for (int o = 0; o < cell->Objects.size(); o++)
            {
                MapObject *ob = cell->Objects[o];
                if (ob->getRace() == ObjectType::Monster || ob->getRace() == ObjectType::Player)
                {
                    if (!ob->IsAttackTarget(this))
                    {
                        continue;
                    }

                    if (push)
                    {
                        ob->Pushed(this, getDirection(), distance - 1);
                    }

                    int delay = Functions::MaxDistance(getCurrentLocation(), ob->getCurrentLocation()) * 50 + additionalDelay; //50 MS per Step
                    DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + delay, {ob, damage, defenceType});
                    ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
                }
                else
                {
                    continue;
                }

                break;
            }
        }
    }

    void MonsterObject::LineAttack(int distance, int additionalDelay, DefenceType defenceType, bool push)
    {
        int damage;

        switch (defenceType)
        {
            case DefenceType::MAC:
            case DefenceType::MACAgility:
                damage = GetAttackPower(Stats[Stat::MinMC], Stats[Stat::MaxMC]);
                break;
            default:
                damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
                break;
        }

        if (damage == 0)
        {
            return;
        }

        for (int i = 1; i <= distance; i++)
        {
            Point *target = Functions::PointMove(getCurrentLocation(), getDirection(), i);

            if (!getCurrentMap()->ValidPoint(target))
            {
                continue;
            }

            Cell *cell = getCurrentMap()->GetCell(target);
            if (cell->Objects.empty())
            {
                continue;
            }

            for (int o = 0; o < cell->Objects.size(); o++)
            {
                MapObject *ob = cell->Objects[o];
                if (ob->getRace() == ObjectType::Monster || ob->getRace() == ObjectType::Player)
                {
                    if (!ob->IsAttackTarget(this))
                    {
                        continue;
                    }

                    if (push)
                    {
                        ob->Pushed(this, getDirection(), distance - 1);
                    }

                    int delay = Functions::MaxDistance(getCurrentLocation(), ob->getCurrentLocation()) * 50 + additionalDelay; //50 MS per Step
                    DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + delay, {ob, damage, defenceType});
                    ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
                }
                else
                {
                    continue;
                }

                break;
            }
        }
    }

    void MonsterObject::HalfmoonAttack(int delay, DefenceType defenceType)
    {
        MirDirection dir = Functions::PreviousDir(getDirection());

        int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
        if (damage == 0)
        {
            return;
        }

        for (int i = 0; i < 4; i++)
        {
            Point *target = Functions::PointMove(getCurrentLocation(), dir, 1);
            dir = Functions::NextDir(dir);

            if (!getCurrentMap()->ValidPoint(target))
            {
                continue;
            }

            Cell *cell = getCurrentMap()->GetCell(target);
            if (cell->Objects.empty())
            {
                continue;
            }

            for (int o = 0; o < cell->Objects.size(); o++)
            {
                MapObject *ob = cell->Objects[o];
                if (ob->getRace() != ObjectType::Player && ob->getRace() != ObjectType::Monster)
                {
                    continue;
                }
                if (!ob->IsAttackTarget(this))
                {
                    continue;
                }

                DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + delay, {ob, damage, defenceType});
                ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
                break;
            }
        }
    }

    void MonsterObject::FullmoonAttack(int delay, DefenceType defenceType)
    {
        int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
        if (damage == 0)
        {
            return;
        }

        MirDirection dir = getDirection();

        for (int i = 0; i < 8; i++)
        {
            dir = Functions::NextDir(dir);
            Point *point = Functions::PointMove(getCurrentLocation(), dir, 1);

            if (!getCurrentMap()->ValidPoint(point))
            {
                continue;
            }

            Cell *cell = getCurrentMap()->GetCell(point);

            if (cell->Objects.empty())
            {
                continue;
            }

            for (int o = 0; o < cell->Objects.size(); o++)
            {
                MapObject *ob = cell->Objects[o];
                if (ob->getRace() != ObjectType::Player && ob->getRace() != ObjectType::Monster)
                {
                    continue;
                }
                if (!ob->IsAttackTarget(this))
                {
                    continue;
                }

                DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + delay, {ob, damage, defenceType});
                ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
                break;
            }
        }
    }

    void MonsterObject::ProjectileAttack(int minAttackStat, int maxAttackStat, DefenceType type, int additionalDelay)
    {
        int damage = GetAttackPower(minAttackStat, maxAttackStat);
        if (damage == 0)
        {
            return;
        }

        int delay = Functions::MaxDistance(getCurrentLocation(), getTarget()->getCurrentLocation()) * 50 + additionalDelay;

        DelayedAction *action = new DelayedAction(DelayedType::RangeDamage, getEnvir()->getTime() + delay, {getTarget(), damage, type});
        ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
    }

    void MonsterObject::SinglePushAttack(int minAttackStat, int MaxAttackStat, DefenceType type, int delay, int pushDistance)
    {
        //Repulsion - (utilises DelayedAction so player is hit at end of push)
        //need to put Damage Stats (DC/MC/SC) on mob for it to push
        int levelGap = 5;
        int mobLevel = this->getLevel();
        int targetLevel = getTarget()->getLevel();

        if ((targetLevel <= mobLevel + levelGap))
        {
            if (getTarget()->Pushed(this, Functions::DirectionFromPoint(getCurrentLocation(), getTarget()->getCurrentLocation()), pushDistance) > 0)
            {
                int damage = GetAttackPower(minAttackStat, MaxAttackStat);
                AttackTime = getEnvir()->getTime() + AttackSpeed + 300;
                if (damage == 0)
                {
                    return;
                }

                DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + delay, {getTarget(), damage, type});
                ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
            }
            else
            {
                int damage = GetAttackPower(minAttackStat, MaxAttackStat);
                if (damage == 0)
                {
                    return;
                }

                DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + delay, {getTarget(), damage, type});
                ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
            }
        }
    }
}
